////////////////////////////////////////////////
// DESCRIPTION:
//     Class for accesing the Windows CE Registry
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#pragma once

#include <devload.h>
#include <ddkreg.h>

namespace FusionWare
{
    namespace Win32
    {        
        // Single entry in a reg value table
        struct RegValueEntry
        {
            bool Required;          // FALSE if default info available else TRUE if must be present
            const TCHAR* ValueName; // Name of value (NULL == default value)
            DWORD RegType;          // REG_DWORD etc...
            size_t OffsetValue;     // offset of the member
            DWORD ValueSize;        // size of Value (0== End of table)
            const RegValueEntry* pChain;  // Pointer to base class registry info
        };

        ////////////////////////////////////////////////
        // Description:
        //    Class for accesing a registry Key
        //
        // Remarks:
        //    Manages an HKEY providing for automatic lose
        //    when containing scope is exited. 
        //
        class RegKey
        {
        public:
            // creates a new instance from an existing HKEY value
            RegKey(HKEY hKeyVal)
                : hKey(hKeyVal)
            {
            }
            
            // create a new instance with no opened key
            RegKey()
                : hKey(NULL) 
            {
            }

            // destroy this instance; Closing the Key
            ~RegKey()   { Close(); }

            operator HKEY() { return this->hKey; }

            ////////////////////////////////////////////////
            // Tests if Key is Valid
            //
            // Returns:
            //    true    Key is *not* valid
            //    false   Key is valid
            //
            operator !() { return this->hKey != NULL; }
            
            //Remarks:
            //   Assigns a new value from an existing opened Key
            //   If the underlying handle is open it is closed 
            //   first.
            //
            RegKey& operator =(HKEY NewKey)
            {
                if(this->hKey != NULL)
                     RegCloseKey(this->hKey);

                this->hKey = NewKey;
				return *this;
            }

            ///////////////////////////////////////////////////////
            // Description: 
            //    Opens an existing Key
            // 
            // Input: 
            //    hRoot     Root Key for the new key to open
            //    pSubKey   Path of key to open of the root (hRoot)
            //
            // Remarks: 
            //    Opens an existing key. This function Fails if the key
            //    does not exist.
            //
            inline bool Open(HKEY hRoot, const TCHAR* pSubKey);

            ///////////////////////////////////////////////////////
            // Description: 
            //    Opens a device Drivers Key
            //
            // Input: 
            //    pRegPath - Path to devices Active Key (provided in the
            //               dwContext parramater of the drivers xxx_Init function.
            //
            // Remarks: 
            //    ActivateDevice will create a registry key for the device
            //    under HKLM\Devices\Active. One of the values it creates
            //    there is the "RegKey" value which is the registry path (under HKLM)
            //    that was used to load the device.
            //   
            //    This method will use the RegKey value to open the original registry
            //    key for the device so that the driver may read the settings
            //    for the device. Or Bus Drivers can add settings for the bus.
            //
            // Note: 
            //     
            inline bool OpenDeviceKey(const TCHAR* pRegPath);

            ///////////////////////////////////////////////////////
            // Description: 
            //    Creates a new Key
            //
            // Input: 
            //    hRoot         Root key for new key
            //    pSubKey       path from root for the new key
            //    Options       [Default = REG_OPTION_NON_VOLATILE]
            //    pClass        [Default = NULL] Class Name for the Key
            //
            // Output:
            //    pDisposition  [Default = NULL] Creation Disposition
            //@table
            //    Value                    Description
            //    -----------------------  -----------
            //    REG_CREATED_NEW_KEY      A new Key was created
            //    REG_OPENED_EXISTING_KEY  The key was found and opened.
            //
            // Remarks: 
            //    Creates a new key from the root. If the key exists it is opened
            //
            inline bool Create( HKEY hRoot
                              , const TCHAR* pSubKey
                              , DWORD Options = REG_OPTION_NON_VOLATILE
                              , DWORD* pDisposition = NULL
                              , const TCHAR* pClass = NULL);

            // Closes the underlying key if it is open
            bool Close();

            //////////////////////////////////////////////////////
            // Description:
            //    Detaches the underlying Key from this instance so it isn't closed
            //    when it goes out of scope
            //
            // Returns:
            //    Underlying HKEY value
            //
            // Remarks:
            //    The underlying key is set to NULL to prevent automatic close
            //    in the destructor.
            //
            HKEY Detach() { HKEY temp = this->hKey; this->hKey=NULL; return temp; }


            ///////////////////////////////////////////////////////
            // Description: 
            //    Reads a set of values from the key
            //
            // Input: 
            //    pEntries   pointer to the table of entries (terminated by a NULL entry)
            //    pBase      Base of oject or structure instance the entries refer to
            //
            // Remarks: 
            //    This function allows the caller to read a complete set of registry 
            //    values in one call and populate the values read into the members
            //    of an object or structure instance.
            //
            // See Also: 
            //    RegValueEntry
            // 
            bool ReadValues(const RegValueEntry* pEntries, BYTE* pBase);

            ///////////////////////////////////////////////////////
            // Description: 
            //    Retrieves the size of the registry entry
            // 
            // Input: 
            //    ValueName  Name of the Value to get the size of
            //
            // Remarks: 
            //    Useful for strings and Binary values to pre-allocate the space
            //    for the value before calling ReadValue.
            // 
            DWORD QueryValueSize(const TCHAR* ValueName);

            ///////////////////////////////////////////////////////
            // Description: 
            //    Read a value from the registry Key
            //
            // Input: 
            //    ValueName  Name of the registry value
            //    pData      Pointer to where the data should go
            //    cbData     Reference to a variable that specifies the size, in bytes,
            //               of the buffer pointed to by the pData parameter.
            // Output:
            //    cbData     When the function returns, this variable contains the size
            //               of the data copied to pData. If the data has the REG_SZ,
            //               REG_MULTI_SZ or REG_EXPAND_SZ type, then cbData will also
            //               include the size of the terminating null character. 
            //               If the buffer specified by pData parameter is not large enough
            //               to hold the data, the function returns the function FAILS, and stores
            //               the required buffer size, in bytes, into the variable referenced
            //               to by cbData. 
            //
            // Output:
            //    cbData     Length of data placed into the pData
            // 
            // Remarks: 
            //    You can use the QueryValueSize method to determine the proper size to 
            //    allocate for the value.
            //
            bool ReadValue(const TCHAR* ValueName, void* pvData, DWORD& cbData);
            
            // Remarks:
            //    Won't override the data pointed to by pData unless a valid value exists in the registry
            //    this allows for setting up of default values first and calling this function
            //    to check for an override.
            //
            bool ReadValue(const TCHAR* ValueName, DWORD* pdwData);
            bool ReadValue(const TCHAR* ValueName, TCHAR* pszData, DWORD& cbData);

            ///////////////////////////////////////////////////////
            // Description: 
            //    Writes a value to the registry
            //
            // Input: 
            //    ValueName  Name of the value in the registry
            //    pData      pointer to the data to write
            //    cbData     count of bytes to write
            //
            // Remarks: 
            //    Writes a Binary value to the registry key
            // 
            inline bool WriteValue(const TCHAR* ValueName, void* pData, DWORD cbData);
            
            // Input:
            //    Data  DWORD value to write to the registry
            //
            // Remarks:
            //    Writes a DWORD to the registry.
            //
            inline bool WriteValue(const TCHAR* Name, DWORD Data);
            
            // Input:
            //    IsMultiSz  Indicates it the string is MULTI_SZ format
            //
            // Remarks:
            //    Write a string to the registry
            //    if cbData==0 (default) then _tcslen() is used to get the length
            //    Length cannot be 0 if it is a Multi SZ
            //
            bool WriteValue(const TCHAR* ValueName, const TCHAR* pData, DWORD cbData=0, bool IsMultiSz = false);

        protected:
            HKEY hKey;
        };

        inline bool RegKey::Open(HKEY hRoot, const TCHAR* pSubKey)
        {
            Close();
            return ERROR_SUCCESS == RegOpenKeyEx(hRoot, pSubKey, 0, 0, &this->hKey);
        }

        inline bool RegKey::OpenDeviceKey(const TCHAR* pRegPath)
        {
            Close();
            this->hKey = ::OpenDeviceKey(pRegPath);
            return this->hKey != NULL; 
        }

        inline bool RegKey::Create(HKEY hRoot, const TCHAR* pSubKey, DWORD Options, DWORD* pDisposition, const TCHAR* pClass)
        {
            DWORD disposition;
            if(NULL==pDisposition)
                pDisposition = &disposition;

            Close();
            return ERROR_SUCCESS == RegCreateKeyEx(hRoot, pSubKey, 0, const_cast<TCHAR*>(pClass), Options, 0, NULL, &this->hKey, pDisposition);
        }

        inline bool RegKey::WriteValue(const TCHAR* ValueName, void* pData, DWORD cbData)
        {
            return ERROR_SUCCESS == RegSetValueEx(this->hKey, ValueName, 0, REG_BINARY, (BYTE*)pData, cbData);
        }

        inline bool RegKey::WriteValue(const TCHAR* Name,DWORD NewVal)
        {
            return ERROR_SUCCESS == RegSetValueEx(this->hKey, Name, 0, REG_DWORD, (BYTE*)&NewVal, sizeof(NewVal));
        }

        /////////////////////////////////////////////////////////////
        // Description:
        //    Class to manage a set of registry values
        //
        // Remarks:
        //    This class is used to read a set of values from the
        //    registry. It is used in conjunction with a 
        //    value entry mapping table.
        //
        // See Also:
        //    DECLARE_REG_VALUE_TABLE, BEGIN_REG_VALUE_TABLE, END_REG_VALUE_TABLE,
        //    REGVAL_DWORD, REGVAL_STR, REGVAL_DWORD_REQ, REGVAL_STR_REQ
        //   
        class RegValues
        {
        public:    
            virtual bool ReadRegValues(RegKey& RootKey);
            
            bool ReadRegValues(RegKey& RootKey, const TCHAR* SubKey);
            bool ReadRegValues(HKEY hRootKey);
            bool ReadRegValues(HKEY hRootKey, const TCHAR* SubKey);
            bool ReadRegValues(const TCHAR* pRegPath);

        protected:
            virtual const RegValueEntry* GetRegValueEntries()=0;
        };

        // BEGIN, and END table Macros for registry value tables along
        // the style of MFC table macros

        //////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        //    Begins the definition of a registry value table
        //
        // Input:
        //    cls  Name of class for which the registry table is a meber of
        //
        // Remarks:
        //    This is used along with the various REG_VAL macros
        //    to establish a mapping table for RegValues
        //
        // Example:
        //    BEGIN_REG_VALUE_TABLE(CDeviceRegValues)
        //        REGVAL_DWORD(_T("IoStride"),    m_IoStride)
        //        REGVAL_DWORD(_T("Flags"),       m_Flags)
        //        REGVAL_DWORD(_T("Priority256"), m_Priority256)
        //        REGVAL_DWORD(_T("EnableWake"),  m_EnableWake)
        //        REGVAL_DWORD(_T("IrqDeviceID"), m_IrqDeviceID)
        //        REGVAL_DWORD(_T("IrqMode"),     m_IrqMode)
        //        REGVAL_DWORD(_T("IrqState"),    m_IrqState)
        //    END_REG_VALUE_TABLE
        //
        #define BEGIN_REG_VALUE_TABLE(cls) \
        private: typedef cls __RegTableClass; \
        public: \
        virtual const FusionWare::Win32::RegValueEntry* GetRegValueEntries() { \
        static const FusionWare::Win32::RegValueEntry regEntries[] = {

        //////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        //    Ends a registry value mapping table
        //
        // Remarks:
        //    Closes the registry value table 
        //
        #define END_REG_VALUE_TABLE \
        {false,NULL,0,0,0} }; return regEntries; }

        // {partof:FusionWare::DFX::RegValues}
        #define REGENTRY_CHAIN 0xFFFF5a5a

        ////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        //     Declares a table chain to a base class
        // 
        // Input:
        //     base - name of base class
        //
        #define REGVAL_CHAIN(base) {false,NULL,REGENTRY_CHAIN,0,0,##base##::GetRegValueEntries()},

        ////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        //    declares an Optional DWORD RegValueEntry
        //
        // Input:
        //    name  - name of the value (const TCHAR*)
        //    oVal  - member name of class/struct c that will
        //            contain the value read from the registry
        #define REGVAL_DWORD(name, oVal)      {false, (name), REG_DWORD, offsetof(__RegTableClass, oVal), sizeof(DWORD)},

        ////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        //    declares an Optional String RegValueEntry
        //
        // Input:
        //    name   - name of the value (const TCHAR*)
        //    oVal   - member name of class/struct c that will
        //             contain the value read from the registry
        //    lenVal - Maximum length of the c.oVal buffer
        //
        #define REGVAL_STR(name, oVal, lenVal) {false, (name), REG_SZ, offsetof(__RegTableClass, oVal), (lenVal)},

        ////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        //    declares a required DWORD RegValueEntry
        //
        // Input:
        //    name   - name of the value (const TCHAR*)
        //    oVal   - member name of class/struct that will
        //             contain the value read from the registry
        #define REGVAL_DWORD_REQ(name, oVal)  {true, (name), REG_DWORD, offsetof(__RegTableClass, oVal), sizeof(DWORD)},

        ////////////////////////////////////////////////
        // {partof:FusionWare::DFX::RegValues}
        // Description:
        // declares a required String RegValueEntry
        //
        // Input:
        //    name   - name of the value (const TCHAR*)
        //    oVal   - member name of class/struct c that will
        //             contain the value read from the registry
        //    lenVal - Maximum length of the c.oVal buffer
        //
        #define REGVAL_STR_REQ(name, oVal,lenVal) {true, (name), REG_SZ, offsetof(__RegTableClass, oVal), (lenVal)},

    }
}